//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Menu stuff.
//

# include "StdAfx.h"
# include "Resource.h"
# include "Menu.h"
# include "Settings.h"
# include "Toolbar.h"
# include "Map.h"
# include "MapView.h"
# include "MiniMap.h"
# include "Selection.h"
# include "Levels.h"
# include "Tool.h"
# include "Paste.h"

# include "GarminTypes.h"
# include "NavitelTypes.h"
# include "RussaTypes.h"

# include "UndoRedo.h"
# include "Log.h"
# include "RecentFiles.h"
# include "Favorites.h"
# include "Load.h"
# include "Positioning.h"
# include "Scale.h"
# include "CreatePOIByAddress.h"

# include "Str.h"
# include "Globals.h"

bool IsSeparator (HMENU _hMenu, int _nPos) {
	const UINT uiState = ::GetMenuState (_hMenu, _nPos, MF_BYPOSITION);
	if ((uiState & MF_SEPARATOR) == 0)
		return false;
	return (uiState & MF_POPUP) == 0;
}

void DeleteDuplicatedMenuSeparators (HMENU _hMenu) {
	bool bFound;
	do {
		bFound = false;

		const int nItems = ::GetMenuItemCount (_hMenu);
		if (nItems == 0)
			return;

		for (int nPos = -1; nPos < nItems; ++ nPos) {
			bool bSeparator0 = nPos     == -1     || IsSeparator (_hMenu, nPos);
			bool bSeparator1 = nPos + 1 >= nItems || IsSeparator (_hMenu, nPos + 1);

			if (bSeparator0 && bSeparator1) {
				::DeleteMenu (_hMenu, nPos == -1 ? 0 : nPos, MF_BYPOSITION);
				bFound = true;
				break;
			}
		}
	} while (bFound);
}

///////////////////////////////////////////////////////////////

static
bool _FindParentMenu (HMENU _hmenuParent, size_t _cPosInParent, HMENU _hmenuThis, UINT _uiCmd, bool _bDelete) {
	const size_t cItems = ::GetMenuItemCount (_hmenuThis);
	for (size_t cPos = 0; cPos < cItems; ++ cPos) {
		const UINT uiCmd = ::GetMenuItemID (_hmenuThis, cPos);
		if (uiCmd == _uiCmd) {
			if (_hmenuParent != NULL) {
				if (_bDelete)
					::DeleteMenu     (_hmenuParent, _cPosInParent, MF_BYPOSITION);
				else
					::EnableMenuItem (_hmenuParent, _cPosInParent, MF_BYPOSITION | MF_GRAYED);
			}
			return true;
		}

		const HMENU hSubMenu = ::GetSubMenu (_hmenuThis, cPos);
		if (hSubMenu != NULL) {
			if (_FindParentMenu (_hmenuThis, cPos, hSubMenu, _uiCmd, _bDelete))
				return true;
		}
	}

	return false;
}

void DeleteParentMenu (HMENU _hMenu, UINT _uiCmd) {
	_FindParentMenu (NULL, 0, _hMenu, _uiCmd, true);
}

void DisableParentMenu (HMENU _hMenu, UINT _uiCmd) {
	_FindParentMenu (NULL, 0, _hMenu, _uiCmd, false);
}

///////////////////////////////////////////////////////////////

const size_t c_cMaxLen = 50;

void InitMenu () {
	assert (g_hWnd);

	const HMENU hMenu = ::GetMenu (g_hWnd);
	g_MainMenu.Init (hMenu);

	const HMENU hmenuFile  = ::GetSubMenu (hMenu, smFile);
	const HMENU hmenuView  = ::GetSubMenu (hMenu, smView);
	const HMENU hmenuTools = ::GetSubMenu (hMenu, smTools);

# ifndef _DEBUG
	::DeleteMenu (hmenuFile, ID_FILE_PAGE_SETUP, MF_BYCOMMAND);
	::DeleteMenu (hmenuFile, ID_FILE_PRINT,      MF_BYCOMMAND);
	DeleteDuplicatedMenuSeparators (hmenuFile);

	::DeleteMenu (hmenuTools, ID_JOIN_ROADS,      MF_BYCOMMAND);
	::DeleteMenu (hmenuTools, ID_GENERATE_RG_END, MF_BYCOMMAND);
	DeleteDuplicatedMenuSeparators (hmenuTools);
# endif // HIDE_ROUTING

	// Init 'Import' and 'Export' menu.
	UpdateImportExportMenu ();

	// Init 'Scales' menu.
	const HMENU hScalesMenu = ::GetSubMenu (hmenuView, smViewScales);
	const size_t cStdScales = GetStdScales ();
	for (size_t cStdScale = 0; cStdScale < cStdScales; ++ cStdScale) {
		char strScaleText [64];
		const bool bText = GetStdScaleName (cStdScale, strScaleText);

		if (bText)
			::AppendMenu (hScalesMenu, MF_STRING, ID_VIEW_SCALE_0 + cStdScale, strScaleText);
		else
			::AppendMenu (hScalesMenu, MF_SEPARATOR, -1, NULL);
	}
	// Delete dummy placeholder.
	::DeleteMenu (hScalesMenu, 0, MF_BYPOSITION);

	// Init 'Favorites' menu.
	UpdateFavoritesMenu ();

	// NOTE: not ready yet.
	::DeleteMenu (hMenu, ID_GENERALIZE_ADJACENT, MF_BYCOMMAND);

	g_MainMenu.UpdateODItems ();
}

HMENU g_hmenuWTR;

void UpdateImportExportMenu () {
	const HMENU hMenu = ::GetMenu (g_hWnd);

	// Init 'Import' menu.
	const HMENU hmenuImport = ::CreatePopupMenu ();
	CFileTypeInfo::InitImportMenu (hmenuImport);
	MENUITEMINFO mii;
	::ZeroMemory (& mii, sizeof (mii));
	mii.cbSize   = sizeof (mii);
	mii.fMask    = MIIM_SUBMENU;
	mii.hSubMenu = hmenuImport;
	::SetMenuItemInfo (hMenu, ID_FILE_IMPORT, FALSE, & mii);

	// Init 'Export' menu.
	const HMENU hmenuExport = ::CreatePopupMenu ();
	g_hmenuWTR = CFileTypeInfo::InitExportMenu (hmenuExport);
	::ZeroMemory (& mii, sizeof (mii));
	mii.cbSize   = sizeof (mii);
	mii.fMask    = MIIM_SUBMENU;
	mii.hSubMenu = hmenuExport;
	::SetMenuItemInfo (hMenu, ID_FILE_EXPORT, FALSE, & mii);
}

void UpdateMenu () {
	const HMENU hMenu = ::GetMenu (g_hWnd);
	const HMENU hViewMenu = ::GetSubMenu (hMenu, smView);

	bool bEmptyMap, bNoMap;
	size_t cLevels;
	bool bRG = false;
	{
		CCritSec::CLock lock (g_cs);
		bEmptyMap = g_map.rectBound.Invalid ();
		bNoMap = g_map.empty ();
		cLevels = g_map.Levels.size ();

		bool bRG = g_map.pTypeSet == & g_tsGarmin || g_map.pTypeSet == & g_tsNavitel || g_map.pTypeSet == & g_tsRussa;
	}

	//
	// 'File' menu.
	//
	::EnableMenuItem (hMenu, ID_FILE_CLOSE,       MF_BYCOMMAND | (bNoMap           || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_FILE_OPEN,        MF_BYCOMMAND | (                    g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_FILE_ADD,         MF_BYCOMMAND | (                    g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_FILE_SAVE,        MF_BYCOMMAND | (! CanSave ()     || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_FILE_SAVE_AS,     MF_BYCOMMAND | (bNoMap           || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_FILE_IMPORT,      MF_BYCOMMAND | (                    g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_FILE_EXPORT,      MF_BYCOMMAND | (bNoMap           || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_FILE_PAGE_SETUP,  MF_BYCOMMAND | (bNoMap           || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_FILE_PRINT,       MF_BYCOMMAND | (bNoMap           || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_MAP_PROPERTIES,   MF_BYCOMMAND | (bEmptyMap        || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_ERROR_LOG,        MF_BYCOMMAND | (IsLogEmpty ()    || g_bLoading ? MF_GRAYED : MF_ENABLED));
	// Recent files.
	for (size_t cID = ID_FILE_RECENT_0; cID <= ID_FILE_RECENT_0 + 9; ++ cID) {
		const bool bIsFileAccessible = ::IsFileAccessible (cID - ID_FILE_RECENT_0);
		::EnableMenuItem (hMenu, cID,             MF_BYCOMMAND | (! bIsFileAccessible || g_bLoading ? MF_GRAYED : MF_ENABLED));
	}

	MENUITEMINFO mii = {sizeof (mii), MIIM_SUBMENU};
	::GetMenuItemInfo (hMenu, ID_FILE_EXPORT, FALSE, & mii);
	::EnableMenuItem (mii.hSubMenu, 0, MF_BYPOSITION | (! g_bHaveWaypointsOrTracks || g_bLoading ? MF_GRAYED : MF_ENABLED));

	//
	// 'Edit' menu.
	//

	// Undo.
	const bool bCanUndo = CanUndo () && ! g_bLoading;
	string_t strText = GetStr (IDS_UNDO_MENU);
	if (bCanUndo) {
		strText.append (" ");
		g_Undo.back ()->GetName (strText);

		char strTextCompact [c_cMaxLen + 32];
		::PathCompactPathEx (strTextCompact, strText.c_str (), c_cMaxLen, 0);
		strText = strTextCompact;
	}
	// Restore accelerator.
	char strUndo [128];
	::GetMenuString (hMenu, ID_EDIT_UNDO, strUndo, ARRAY_SIZE (strUndo), MF_BYCOMMAND);
	const char * const pUndoAcc = lstrchr (strUndo, '\t');
	if (pUndoAcc)
		strText.append (pUndoAcc);
	::ModifyMenu (hMenu, ID_EDIT_UNDO, MF_BYCOMMAND | MF_STRING | (bCanUndo ? MF_ENABLED : MF_GRAYED), ID_EDIT_UNDO, strText.c_str ());

	// Redo.
	const bool bCanRedo = CanRedo () && ! g_bLoading;
	strText = GetStr (IDS_REDO_MENU);
	if (bCanRedo) {
		strText.append (" ");
		g_Redo.back ()->GetName (strText);

		char strTextCompact [c_cMaxLen + 32];
		::PathCompactPathEx (strTextCompact, strText.c_str (), c_cMaxLen, 0);
		strText = strTextCompact;
	}
	// Restore accelerator.
	char strRedo [128];
	::GetMenuString (hMenu, ID_EDIT_REDO, strRedo, ARRAY_SIZE (strRedo), MF_BYCOMMAND);
	const char * const pRedoAcc = lstrchr (strRedo, '\t');
	if (pRedoAcc)
		strText.append (pRedoAcc);
	::ModifyMenu (hMenu, ID_EDIT_REDO, MF_BYCOMMAND | MF_STRING | (bCanRedo ? MF_ENABLED : MF_GRAYED), ID_EDIT_REDO, strText.c_str ());

	bool bCanCopy = false;
	if (! g_Selection.empty ()) {
		const size_t cSelItems = g_Selection.size ();
		for (size_t cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
			const find_info_t & SelItem = g_Selection [cSelItem];
			if (SelItem.pLayer && ! SelItem.iRgn->IsDeleted ()) {
				if (! SelItem.iRgn->IsWTR ()) {
					bCanCopy = true;
					break;
				}
			}
		}
	}

	::EnableMenuItem (hMenu, ID_EDIT_CUT,               MF_BYCOMMAND | (! bCanCopy           || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_EDIT_COPY,              MF_BYCOMMAND | (! bCanCopy           || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_EDIT_PASTE,             MF_BYCOMMAND | (IsClipboardEmpty ()  || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_EDIT_DELETE,            MF_BYCOMMAND | (g_Selection.empty () || g_bLoading ? MF_GRAYED : MF_ENABLED));

	::EnableMenuItem (hMenu, ID_SELECT_ALL_OBJECTS,     MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SELECT_ALL_TRACKS,      MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SELECT_ALL_WPTS,        MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SELECT_ALL_ROUTES,      MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SELECT_ALL_RASTERS,     MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SELECT_ALL_ATTACHMENTS, MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_CUSTOM_FILTER,          MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_UNSELECT,               MF_BYCOMMAND | (g_Selection.empty () || g_bLoading ? MF_GRAYED : MF_ENABLED));

	bool bInvertSelectionDisabled = g_Selection.empty () || g_bLoading || IsInvertSelectionDisabled ();
	::EnableMenuItem (hMenu, ID_INVERT_SELECTION,       MF_BYCOMMAND | (bInvertSelectionDisabled ? MF_GRAYED : MF_ENABLED));

	//
	// 'View' menu.
	//
	::EnableMenuItem (hMenu, ID_ZOOM_IN,                MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_ZOOM_OUT,               MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_ZOOM_FULL,              MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SHOW_MINI,              MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SHOW_RULER,             MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SHOW_GRID,              MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SHOW_LABELS,            MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_TRANSPARENT_POLYGONS,   MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SHOW_ATTACHMENTS,       MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SHOW_RGNODES,           MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SHOW_ROAD_CLASSES,      MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_HIGHLIGHT_ADDRESSES,    MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SHOW_NUMBERS,           MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SHOW_GOOGLEMAPS,        MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));

	::EnableMenuItem (hMenu, ID_GPS_TRACKING,           MF_BYCOMMAND | (false                              ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_WTR,                    MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_ATTACHMENTS,            MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));

	::EnableMenuItem (hMenu, ID_REFRESH,                MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_GOTO_POSITION,          MF_BYCOMMAND | (bEmptyMap            || g_bLoading ? MF_GRAYED : MF_ENABLED));

	// 'Scale' sub-menu.
	::EnableMenuItem (hViewMenu, smViewScales,          MF_BYPOSITION | (bEmptyMap           || g_bLoading ? MF_GRAYED : MF_ENABLED));
	if (g_hScaleCB)
		::EnableWindow (g_hScaleCB, bEmptyMap || g_bLoading ? FALSE : TRUE);

	// 'Levels' sub-menu.
	::EnableMenuItem (hViewMenu, smViewLevels,          MF_BYPOSITION | (bEmptyMap           || g_bLoading ? MF_GRAYED : MF_ENABLED));
	UpdateLevelsMenu ();

	::CheckMenuItem (hMenu, ID_SHOW_MINI,               MF_BYCOMMAND | (::IsWindowVisible (g_hMiniMapWnd) ? MF_CHECKED : MF_UNCHECKED));
	::CheckMenuItem (hMenu, ID_SHOW_RULER,              MF_BYCOMMAND | (g_bShowRuler              ? MF_CHECKED : MF_UNCHECKED));
	::CheckMenuItem (hMenu, ID_SHOW_GRID,               MF_BYCOMMAND | (g_bShowGridLines          ? MF_CHECKED : MF_UNCHECKED));
	::CheckMenuItem (hMenu, ID_SHOW_LABELS,             MF_BYCOMMAND | (g_bShowLabels             ? MF_CHECKED : MF_UNCHECKED));
	::CheckMenuItem (hMenu, ID_TRANSPARENT_POLYGONS,    MF_BYCOMMAND | (g_bTransparentPolygons    ? MF_CHECKED : MF_UNCHECKED));
	::CheckMenuItem (hMenu, ID_SHOW_ATTACHMENTS,        MF_BYCOMMAND | (g_bShowAttachments        ? MF_CHECKED : MF_UNCHECKED));
	::CheckMenuItem (hMenu, ID_SHOW_RGNODES,            MF_BYCOMMAND | (g_bShowRGNodes            ? MF_CHECKED : MF_UNCHECKED));
	::CheckMenuItem (hMenu, ID_SHOW_ROAD_CLASSES,       MF_BYCOMMAND | (g_bShowRoadClasses        ? MF_CHECKED : MF_UNCHECKED));
	::CheckMenuItem (hMenu, ID_HIGHLIGHT_ADDRESSES,     MF_BYCOMMAND | (g_bHighlightAddresses     ? MF_CHECKED : MF_UNCHECKED));
	::CheckMenuItem (hMenu, ID_SHOW_NUMBERS,            MF_BYCOMMAND | (g_bShowNumbers            ? MF_CHECKED : MF_UNCHECKED));
	::CheckMenuItem (hMenu, ID_SHOW_GOOGLEMAPS,         MF_BYCOMMAND | (g_bShowGoogleMaps         ? MF_CHECKED : MF_UNCHECKED));
	::CheckMenuItem (hMenu, ID_GPS_TRACKING,            MF_BYCOMMAND | (IsGPSUnitActive ()        ? MF_CHECKED : MF_UNCHECKED));

	//
	// 'Favorites' menu.
	//
	::EnableMenuItem (hMenu, ID_ADD_TO_FAVORITES,       MF_BYCOMMAND | (bEmptyMap   || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_REMOVE_FAVORITE,        MF_BYCOMMAND | (bEmptyMap   || g_bLoading ? MF_GRAYED : MF_ENABLED));
	UpdateFavoritesMenu_Enable ();

	//
	// 'Tools' menu.
	//
	static UINT c_uiToolCmdId [] = {
		ID_TOOL_DRAG,
		ID_TOOL_ZOOM,
		ID_TOOL_SELECT,
		ID_TOOL_EDIT_NODES,
		ID_TOOL_CREATE_RGN,
		ID_TOOL_TRIM,
		ID_TOOL_TRANSFORM,
		ID_TOOL_ROUTING,
	};
	for (size_t cTool = 0; cTool < ARRAY_SIZE (c_uiToolCmdId); ++ cTool) {
		::EnableMenuItem (hMenu, c_uiToolCmdId [cTool], MF_BYCOMMAND | (g_bLoading ? MF_GRAYED : MF_ENABLED));

		MENUITEMINFO mii;
		::memset (& mii, 0, sizeof (mii));
		mii.cbSize  = sizeof (mii);
		mii.fMask   = MIIM_FTYPE | MIIM_STATE;
		mii.fType   = MFT_RADIOCHECK;
		mii.fState &= ~MFS_CHECKED;
		::SetMenuItemInfo (hMenu, c_uiToolCmdId [cTool], FALSE, & mii);
	}

	UINT uiToolCmd = 0;
	switch (CTool::GetCurrentToolId ()) {
		default: assert (false);
		case toolDummy:
		case toolDrag:      uiToolCmd = ID_TOOL_DRAG;       break;
		case toolZoom:      uiToolCmd = ID_TOOL_ZOOM;       break;
		case toolSelect:    uiToolCmd = ID_TOOL_SELECT;     break;
		case toolEditNodes: uiToolCmd = ID_TOOL_EDIT_NODES; break;
		case toolCreate:    uiToolCmd = ID_TOOL_CREATE_RGN; break;
		case toolTrim:      uiToolCmd = ID_TOOL_TRIM;       break;
		case toolTransform: uiToolCmd = ID_TOOL_TRANSFORM;  break;
		case toolRouting:   uiToolCmd = ID_TOOL_ROUTING;    break;
	}
	::CheckMenuRadioItem (hMenu, uiToolCmd, uiToolCmd, uiToolCmd, MF_BYCOMMAND);

	::EnableMenuItem (hMenu, ID_GENERALIZE_POLY,       MF_BYCOMMAND | (bEmptyMap || cLevels == 0 || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_GENERALIZE_ADJACENT,   MF_BYCOMMAND | (bEmptyMap || cLevels == 0 || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_JOIN_RGN_LEVELS,       MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SPLIT_RGN_LEVELS,      MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_REMOVE_DUPLICATES,     MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_CONVERT_LABELS,        MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_REMOVE_HOLES,          MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SLICE_MAP,             MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_SPLIT_MAP,             MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_VERIFY_MAP,            MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_GENERATE_RG,           MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_GENERATE_RG_END,       MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_REMOVE_RG,             MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_CLEAN_UP_EXT_NODES,    MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_FIX_ROUTE_PARAMS,      MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_JOIN_ROADS,            MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_EDIT_PAI,              MF_BYCOMMAND | (bEmptyMap || g_bLoading ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_CREATE_POI_BY_ADDRESS, MF_BYCOMMAND | (bEmptyMap || g_bLoading || ! CreatePOIByAddressIsAvailable () ? MF_GRAYED : MF_ENABLED));
	::EnableMenuItem (hMenu, ID_OPTIONS,               MF_BYCOMMAND | (false                   ? MF_GRAYED : MF_ENABLED));

	// Update the menu bar.
	g_MainMenu.UpdateODItems ();
	::DrawMenuBar (g_hWnd);

	// Update toolbar.
	const size_t cBtns = ::SendMessage (g_hToolbarWnd, TB_BUTTONCOUNT, 0, 0);
	for (size_t cBtn = 0; cBtn < cBtns; ++ cBtn) {
		TBBUTTON tbb;
		::SendMessage (g_hToolbarWnd, TB_GETBUTTON, cBtn, reinterpret_cast<LPARAM> (& tbb));
		const int nStyle = tbb.fsStyle & 0xF;
		if (nStyle == TBSTYLE_BUTTON || nStyle == TBSTYLE_GROUP)
			::SendMessage (g_hToolbarWnd, TB_PRESSBUTTON,  tbb.idCommand, MAKELONG (::GetMenuState (hMenu, tbb.idCommand, MF_BYCOMMAND) & MF_CHECKED ? TRUE : FALSE, 0));
		if (nStyle == TBSTYLE_CHECK)
			::SendMessage (g_hToolbarWnd, TB_CHECKBUTTON,  tbb.idCommand, MAKELONG (::GetMenuState (hMenu, tbb.idCommand, MF_BYCOMMAND) & MF_CHECKED ? TRUE : FALSE, 0));
		if (nStyle != TBSTYLE_SEP)
			::SendMessage (g_hToolbarWnd, TB_ENABLEBUTTON, tbb.idCommand, MAKELONG (::GetMenuState (hMenu, tbb.idCommand, MF_BYCOMMAND) & MF_GRAYED  ? FALSE : TRUE, 0));
	}
}

void PopupMenu (HMENU _hMenu) {
	POINT pt;
	::GetCursorPos (& pt);
	::TrackPopupMenu (
		_hMenu,
		TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON,
		pt.x, pt.y,
		0, g_hWnd, NULL
	);
}

////////////////////////////////////////////////////////////////////////////

static
int _GetMenuItemCount (HMENU _hMenu) {
	size_t cCount = 0;
	for (size_t c = 0; true; ++ c) {
		MENUITEMINFO mii;
		::memset (& mii, 0, sizeof (mii));
		mii.cbSize = sizeof (mii);
		mii.fMask  = MIIM_FTYPE;
		if (! ::GetMenuItemInfo (_hMenu, c, TRUE, & mii))
			break;

		++ cCount;
	}

	return cCount;
}

CMenuBase::CMenuBase () :
	m_bToDestroy (false),
	m_bHorzMenu (true),
	m_hMenu (NULL),
	m_hwndToolbar (NULL),
	m_pMenuItems (NULL),
	m_cMenuItems (0),
	m_hilIcons (NULL)
{}

CMenuBase::~CMenuBase () {
	_DestroyMenuStrings ();

	if (m_hilIcons) {
		::ImageList_Destroy (m_hilIcons);
		m_hilIcons = NULL;
	}

	if (m_bToDestroy)
		::DestroyMenu (m_hMenu);
}

CPopupMenu::CPopupMenu () {
	assert (m_hMenu == NULL);
	m_hMenu = ::CreatePopupMenu ();
	m_bToDestroy = true;
	m_bHorzMenu = false;
}

CPopupMenu::CPopupMenu (HMENU _hMenu) {
	assert (m_hMenu == NULL);
	m_hMenu = _hMenu;
	m_bToDestroy = false;
	m_bHorzMenu = false;
}

int CMenuBase::ShowAsPopup (HWND _hwndParent, RECT & _rectExclude) const {
	TPMPARAMS tpm;
	tpm.cbSize    = sizeof (tpm);
	tpm.rcExclude = _rectExclude;
	return (int) ::TrackPopupMenuEx (m_hMenu, TPM_RIGHTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL | TPM_RETURNCMD, _rectExclude.right, _rectExclude.bottom, _hwndParent, & tpm);
}

void CMenuBase::_DestroyMenuStrings () {
	const size_t cMenuStrings = m_MenuStrings.size ();
	for (size_t c = 0; c < cMenuStrings; ++ c)
		delete [] m_MenuStrings [c];
	m_MenuStrings.clear ();
}

void CMenuBase::AppendItem (WORD _wCmdId, const char * _strItem) {
	::AppendMenu (m_hMenu, MF_STRING, _wCmdId, _strItem);
}

void CMenuBase::EnableItem (WORD _wCmdId, bool _bEnable) {
//	if (m_hwndToolbar)
//		::SendMessage (m_hwndToolbar, TB_SETSTATE, _wCmdId, _bEnable ? TBSTATE_ENABLED : 0);

	assert (m_hMenu);
	if (m_hMenu)
		::EnableMenuItem (m_hMenu, _wCmdId, MF_BYCOMMAND | (_bEnable ? MF_ENABLED : MF_GRAYED));
}

void CMenuBase::CheckItem (WORD _wCmdId, bool _bCheck) {
//	if (m_hwndToolbar)
//		::SendMessage (m_hwndToolbar, TB_SETSTATE, _wCmdId, TBSTATE_ENABLED | (_bCheck ? TBSTATE_CHECKED : 0));

	assert (m_hMenu);
	if (m_hMenu)
		::CheckMenuItem (m_hMenu, _wCmdId, MF_BYCOMMAND | (_bCheck ? MF_CHECKED : MF_UNCHECKED));
}

void CMenuBase::SetDefaultItem (WORD _wCmdId) {
	assert (m_hMenu);
	if (m_hMenu) {
		MENUITEMINFO mii;
		::memset (& mii, 0, sizeof (mii));
		mii.cbSize = sizeof (mii);
		mii.fMask  = MIIM_STATE;
		::GetMenuItemInfo (m_hMenu, _wCmdId, FALSE, & mii);
		mii.fState |= MFS_DEFAULT;
		::SetMenuItemInfo (m_hMenu, _wCmdId, FALSE, & mii);
	}
}

/*void CMenuBase::SetIcons (UINT _uiIconsBmp, const menuitem_data_t * _pMenuItems, size_t _cMenuItems) {
	m_pMenuItems = _pMenuItems;
	m_cMenuItems = _cMenuItems;

	assert (m_hilIcons == NULL);
	if (_uiIconsBmp != 0)
		m_hilIcons = LoadImageList (_uiIconsBmp, 16, c_colLtGray);

	if (GetToolbar () && m_hilIcons != NULL)
		::SendMessage (GetToolbar (), TB_SETIMAGELIST, 0, reinterpret_cast<LPARAM> (m_hilIcons));
}*/

size_t CMenuBase::_FindImageForCmd (WORD _wCmdId) const {
	if (! m_pMenuItems)
		return -1;

	for (size_t c = 0; c < m_cMenuItems; ++ c)
		if (m_pMenuItems [c].wCmdId == _wCmdId)
			return m_pMenuItems [c].wImage;

	return -1;
}

void CMenuBase::UpdateODItems () {
//	_MakeMenuItemsOD (m_hMenu, m_bHorzMenu);
}

void CMenuBase::_MakeMenuItemsOD (HMENU _hMenu, bool _bHorzMenu) {
	const size_t cItems = _GetMenuItemCount (_hMenu);
	if (cItems == 0)
		return;

	for (size_t c = 0; c < cItems; ++ c) {
		MENUITEMINFO mii;
		::memset (& mii, 0, sizeof (mii));
		mii.cbSize = sizeof (mii);
		mii.fMask  = MIIM_FTYPE | MIIM_SUBMENU;
		if (::GetMenuItemInfo (_hMenu, c, TRUE, & mii) == FALSE)
			continue;

		if (mii.hSubMenu != NULL) {
			// NOTE: apply to sub-menu recursively.
			_MakeMenuItemsOD (mii.hSubMenu, false);
			if (_bHorzMenu)
				continue;
		}

		if (mii.fType != MFT_STRING)
			continue;
					
		TCHAR strText [64] = _T ("");

		// Get menu item string.
		::memset (& mii, 0, sizeof (mii));
		mii.cbSize     = sizeof (mii);
		mii.fMask      = MIIM_TYPE;
		mii.fType      = MFT_STRING;
		mii.dwTypeData = strText;
		mii.cch        = sizeof (strText)/sizeof (strText [0]);
		if (::GetMenuItemInfo (_hMenu, c, TRUE, & mii) == FALSE)
			break;

		// Save menu item string to refer it via dwItemData.
		TCHAR * const strTextP = new TCHAR [mii.cch + 1];
		if (mii.cch)
			::_tcsncpy (strTextP, mii.dwTypeData, mii.cch);
		strTextP [mii.cch] = _T ('\0');
		m_MenuStrings.push_back (strTextP);

		// Set MFT_OWNERDRAW type.
		::memset (& mii, 0, sizeof (mii));
		mii.cbSize = sizeof (mii);
		mii.fMask  = MIIM_FTYPE | MIIM_DATA;
		mii.fType  = MFT_OWNERDRAW;
		mii.dwItemData = reinterpret_cast<DWORD> (strTextP);
		::SetMenuItemInfo (_hMenu, c, TRUE, & mii);
	}
}

const int c_x_icon0   = 3;
const int c_y_icon0   = 2;
const int c_x_icon1   = c_x_icon0 + 18;
const int c_y_icon1   = c_y_icon0 + 16;

const int c_x_text0   = c_x_icon1 + 3;
const int c_y_text0   = 2;

const int c_x_padding = 0;
//const int c_w_acc     = 64;

void CMenuBase::OnMenuMeasureItem (LPARAM _lParam) const {
	MEASUREITEMSTRUCT * const pMI = reinterpret_cast<MEASUREITEMSTRUCT *> (_lParam);

	// Get text.
	const TCHAR * const strText = reinterpret_cast<TCHAR *> (pMI->itemData);
	size_t cLen = ::_tcslen (strText);
	const TCHAR * const pTab = ::_tcschr (strText, _T ('\t'));
	if (pTab)
		cLen = pTab - strText;

	CClientDC hdcScreen (NULL);

	::SelectObject (hdcScreen, ::GetStockObject (SYSTEM_FONT));

	// Determine the text width.
	RECT rectText = {0, 0, 1, 1};
	::DrawText (hdcScreen, strText, cLen, & rectText, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_CALCRECT);

	const int nH = ::GetSystemMetrics (SM_CYMENU);
	const int c_w_acc = nH*8/3;

	pMI->itemWidth = c_x_text0 + (rectText.right - rectText.left) + c_x_padding;
	if (pTab)
		pMI->itemWidth += c_w_acc;

	pMI->itemHeight = nH;
}

void CMenuBase::OnMenuDrawItem (LPARAM _lParam) const {
	const DRAWITEMSTRUCT * const pDI = reinterpret_cast<DRAWITEMSTRUCT *> (_lParam);

	const HDC hDC = pDI->hDC;
	const int x = pDI->rcItem.left;
	const int y = pDI->rcItem.top;
	const HMENU hmenu = reinterpret_cast<HMENU> (pDI->hwndItem);
	const size_t cItemID = pDI->itemID;

	const bool bSelected = (pDI->itemState & ODS_SELECTED) != 0;
	const bool bChecked  = (pDI->itemState & ODS_CHECKED ) != 0;
	const bool bGrayed   = (pDI->itemState & ODS_GRAYED  ) != 0;

	// Erase background.
	::FillRect (
		pDI->hDC, & pDI->rcItem,
		::GetSysColorBrush (bSelected ? COLOR_HIGHLIGHT : COLOR_MENU)
	);

	HBRUSH hbrushPrev = NULL;
	HPEN   hpenPrev   = NULL;

	// Detect radiobutton-style item.
	MENUITEMINFO mii;
	::memset (& mii, 0, sizeof (mii));
	mii.cbSize = sizeof (mii);
	mii.fMask  = MIIM_FTYPE;
	::GetMenuItemInfo (hmenu, cItemID, FALSE, & mii);
	const bool bRadioCheck = (mii.fType & MFT_RADIOCHECK) != 0;

	if (bRadioCheck) {
		if (bChecked) {
			// Draw option mark.
			hbrushPrev = (HBRUSH) ::SelectObject (hDC, ::GetStockObject (BLACK_BRUSH));
			hpenPrev   = (HPEN)   ::SelectObject (hDC, ::GetStockObject (BLACK_PEN));

			const int xc = x + (c_x_icon0 + c_x_icon1)/2;
			const int yc = (pDI->rcItem.top + pDI->rcItem.bottom)/2;
			const int r = 3;
			::Ellipse (
				hDC,
				xc - r,     yc - r,
				xc + r - 1, yc + r - 1
			);
		}
	} else {
		// Draw checkmark frame, if any.
		if (bChecked) {
			RECT rectCheck = {
				x + c_x_icon0, y + c_y_icon0,
				x + c_x_icon1, y + c_y_icon1
			};
			::DrawFrameControl (hDC, & rectCheck, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_CHECKED);
		} else {
			// Draw icon.
			HIMAGELIST hil = m_hilIcons;
			size_t cImageId = _FindImageForCmd (cItemID);
			if (cImageId == -1) {
				// Try to find icon in toolbar.
				hil = reinterpret_cast<HIMAGELIST> (::SendMessage (GetToolbar (), TB_GETIMAGELIST, 0, 0));
				cImageId = ::SendMessage (GetToolbar (), TB_GETBITMAP, cItemID, 0);
				if (cImageId == 0)
					cImageId = -1;
			}
			if (hil && cImageId != -1) {
				::ImageList_Draw (
					hil, cImageId, hDC,
					x + c_x_icon0,
					y + c_y_icon0,
					ILD_TRANSPARENT | (bGrayed ? ILD_SELECTED : 0)
				);
			}
		}
	}

	// Get text.
	const TCHAR * const strTextFull = reinterpret_cast<TCHAR *> (pDI->itemData);

	const size_t cLen = ::_tcslen (strTextFull);
	TCHAR * const strText = (TCHAR *) alloca ((cLen + 1)*sizeof (TCHAR));
	::_tcscpy (strText, strTextFull);

	// Truncate \t.
	TCHAR * pAccelerator = ::_tcschr (strText, _T ('\t'));
	if (pAccelerator) {
		* pAccelerator = _T ('\0');
		++ pAccelerator;
	}

	// Prepare to text drawing.
	const int nTextColor = bGrayed ? COLOR_GRAYTEXT : (bSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT);
	const COLORREF clrPrevText = ::SetTextColor (hDC, ::GetSysColor (nTextColor));

	const int nBkModePrev = ::SetBkMode (hDC, TRANSPARENT);

	const int c_w_acc = ::GetSystemMetrics (SM_CYMENU)*8/3;

	::SelectObject (hDC, ::GetStockObject (SYSTEM_FONT));

	// Draw the text.
	RECT rectText = {
		x + c_x_text0,
		y + c_y_text0,
		pDI->rcItem.right - ((pAccelerator ? c_w_acc : 0) + c_x_padding),
		pDI->rcItem.bottom
	};
	::DrawText (hDC, strText, -1, & rectText, DT_LEFT | DT_TOP | DT_SINGLELINE);

/*{
	RECT rectText = {0, 0, 1, 1};
	::DrawText (hDC, strText, -1, & rectText, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_CALCRECT);
	int w = rectText.right - rectText.left;
	assert (w <= pDI->rcItem.right - pDI->rcItem.left);
}*/

	if (pAccelerator) {
		RECT rectAccelerator = {
			pDI->rcItem.right - c_w_acc,
			y + c_y_text0,
			pDI->rcItem.right - c_x_padding,
			pDI->rcItem.bottom
		};
		::DrawText (hDC, pAccelerator, -1, & rectAccelerator, DT_LEFT | DT_TOP | DT_SINGLELINE);
	}

	// Restore the original HDC state.
	::SetTextColor (hDC, clrPrevText);
	::SetBkMode    (hDC, nBkModePrev);

	if (hbrushPrev)
		::SelectObject (hDC, hbrushPrev);
	if (hpenPrev)
		::SelectObject (hDC, hpenPrev);
}

////////////////////////////////////////////////

CMainMenu g_MainMenu;

void CMainMenu::Init (HMENU _hMenu) {
	m_hMenu = _hMenu;

	// Prepare menu icons.
	m_bHorzMenu = true;
	UpdateODItems ();
}

void CMainMenu::SetToolbar (HWND _hwndToolbar) {
	m_hwndToolbar = _hwndToolbar;
}
